
module spi_ctrl_regs(
    // APB interface
    input                               pclk_i,
    input                               prstn_i,
    input  [31:0]                       paddr_i,
    input                               penable_i,
    input                               psel_i,
    input  [2 :0]                       pprot_i,
    input  [31:0]                       pwdata_i,
    input                               pwrite_i,
    output reg [31:0]                   prdata_o,

    // state ctrl
    input                               rx_1b_data_done_i,
    input                               op_done_i,
    input                               op_err_i,
    input  [7 :0]                       rdata_i,
    input  [2 :0]                       err_state_i,

    // spi 时钟配置信号
    output reg [31:0]                   clk_div_o,
    output reg                          clk_mode_o,

    // 页编程请求
    output reg                          w1B_req_o,

    // 读请求
    output reg                          r_req_o,
    output reg                          r_sreg1_req_o,
    output reg                          r_id_req_o,   

    // 擦除请求
    output reg                          ce_req_o,  
    output reg                          se_req_o,  
    output reg                          be32_req_o,
    output reg                          be64_req_o,

    // 传输控制信号
    output reg [31:0]                   addr_o,    
    output reg [7 :0]                   data_len_o
);

// 寄存器偏移
localparam                              COMD_REG_OFFSET         = 4'd0;
localparam                              ADDR_REG_OFFSET         = 4'd1;
localparam                              DATA_LEN_REG_OFFSET     = 4'd2;
localparam                              SPICLK_DIV_REG_OFFSET   = 4'd3;
localparam                              SPICLK_MODE_REG_OFFSET  = 4'd4;

localparam                              DEVICE_ID_REG_OFFSET    = 4'd5;
localparam                              STATUS_REG_OFFSET       = 4'd6;

localparam                              START_REG_OFFSET        = 4'd7;  
localparam                              CONFIG_REG_OFFSET       = 4'd8; 

// 命令寄存器指令编码
localparam                              INST_NONE               = 4'd0;
localparam                              INST_WRITE1B            = 4'd1;
localparam                              INST_READ               = 4'd2;
localparam                              INST_READ_SREG1         = 4'd3;
localparam                              INST_READID             = 4'd4;
localparam                              INST_CE                 = 4'd5;
localparam                              INST_SE                 = 4'd6;
localparam                              INST_BE32               = 4'd7;
localparam                              INST_BE64               = 4'd8;

// 寄存器组声明
reg [31:0]                              comd_reg;
reg [31:0]                              addr_reg;
reg [31:0]                              data_len_reg;
reg [31:0]                              spiclk_div_reg;
reg [31:0]                              spiclk_mode_reg;                // spiclk_mode_reg = {32'd0, clk_mode}
reg [31:0]                              start_reg;                      // start_reg = {31'd0, start_flag}  脉冲信号
reg [31:0]                              config_reg;                     // config_reg = {29'd0, clk_mode_reset, clk_mode_set, clk_div_reset, clk_div_set} 这个寄存器的每一位是一个脉冲
// 只读寄存器
reg [31:0]                              device_id_reg;                  // device_id_reg = {16'd0, manufacturerID, deviceID}
reg [31:0]                              status_reg;                     // status_reg = {27'd0, err_state3, err_flag1, done_flag1}


wire                                    comd_reg_wen;
wire                                    addr_reg_wen;
wire                                    data_len_reg_wen;
wire                                    spiclk_div_reg_wen;
wire                                    spiclk_mode_reg_wen;
wire                                    start_reg_wen;
wire                                    config_reg_wen;

wire                                    clk_div_set;
wire                                    clk_div_reset;
wire                                    clk_mode_set;
wire                                    clk_mode_reset;
wire                                    start_flag;

reg  [15:0]                             device_id;

// APB总线写寄存器组
assign comd_reg_wen         = ((psel_i == 1'b1) && (penable_i == 1'b1) && (pwrite_i == 1'b1) && (paddr_i[5:2] == COMD_REG_OFFSET)) ? 1'b1 : 1'b0;
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        comd_reg    <= 'd0;
    end
    else if(comd_reg_wen)begin
        comd_reg    <= pwdata_i;
    end
end

assign addr_reg_wen         = ((psel_i == 1'b1) && (penable_i == 1'b1) && (pwrite_i == 1'b1) && (paddr_i[5:2] == ADDR_REG_OFFSET)) ? 1'b1 : 1'b0;
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        addr_reg    <= 'd0;
    end
    else if(addr_reg_wen)begin
        addr_reg    <= pwdata_i;
    end
end

assign data_len_reg_wen     = ((psel_i == 1'b1) && (penable_i == 1'b1) && (pwrite_i == 1'b1) && (paddr_i[5:2] == DATA_LEN_REG_OFFSET)) ? 1'b1 : 1'b0;
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        data_len_reg    <= 'd1;
    end
    else if(data_len_reg_wen)begin
        data_len_reg    <= (pwdata_i == 32'd0) ? 'd1 : pwdata_i;
    end
end

assign spiclk_div_reg_wen    = ((psel_i == 1'b1) && (penable_i == 1'b1) && (pwrite_i == 1'b1) && (paddr_i[5:2] == SPICLK_DIV_REG_OFFSET)) ? 1'b1 : 1'b0;
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        spiclk_div_reg    <= 'd4;
    end
    else if(spiclk_div_reg_wen)begin
        spiclk_div_reg    <= pwdata_i;
    end
end

assign spiclk_mode_reg_wen    = ((psel_i == 1'b1) && (penable_i == 1'b1) && (pwrite_i == 1'b1) && (paddr_i[5:2] == SPICLK_MODE_REG_OFFSET)) ? 1'b1 : 1'b0;
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        spiclk_mode_reg    <= 'd1;
    end
    else if(spiclk_mode_reg_wen)begin
        spiclk_mode_reg    <= pwdata_i;
    end
end

assign start_reg_wen          = ((psel_i == 1'b1) && (penable_i == 1'b1) && (pwrite_i == 1'b1) && (paddr_i[5:2] == START_REG_OFFSET)) ? 1'b1 : 1'b0;
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        start_reg    <= 'd0;
    end
    else if(start_reg[0])begin
        start_reg    <= 'd0;
    end
    else if(start_reg_wen)begin
        start_reg    <= pwdata_i;
    end
end

assign config_reg_wen         = ((psel_i == 1'b1) && (penable_i == 1'b1) && (pwrite_i == 1'b1) && (paddr_i[5:2] == CONFIG_REG_OFFSET)) ? 1'b1 : 1'b0;
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        config_reg    <= 'd0;
    end
    else if(config_reg != 'd0)begin
        config_reg    <= 'd0;
    end
    else if(config_reg_wen)begin
        config_reg    <= pwdata_i;
    end
end

// APB总线读寄存器组
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        prdata_o      <= 'd0;
    end
    else if((psel_i == 1'b1) && (penable_i == 1'b0) && (pwrite_i == 1'b0))begin
        case (paddr_i[5:2])
            COMD_REG_OFFSET         : prdata_o <= comd_reg          ;
            ADDR_REG_OFFSET         : prdata_o <= addr_reg          ;
            DATA_LEN_REG_OFFSET     : prdata_o <= data_len_reg      ;
            SPICLK_DIV_REG_OFFSET   : prdata_o <= spiclk_div_reg    ;
            SPICLK_MODE_REG_OFFSET  : prdata_o <= spiclk_mode_reg   ;
            DEVICE_ID_REG_OFFSET    : prdata_o <= device_id_reg     ;
            STATUS_REG_OFFSET       : prdata_o <= status_reg        ;
            START_REG_OFFSET        : prdata_o <= start_reg         ;
            CONFIG_REG_OFFSET       : prdata_o <= config_reg        ;
            default                 : prdata_o <= 'd0               ;
        endcase
    end
    else begin
        prdata_o      <= 'd0;
    end
end

// APB只读寄存器的写操作（flash写）
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        device_id       <= 'd0;
    end
    else if(r_id_req_o & rx_1b_data_done_i)begin
        device_id       <= {device_id[7:0], rdata_i};
    end
end
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        device_id_reg   <= 'd0;
    end
    else if(r_id_req_o & op_done_i)begin
        device_id_reg   <= {16'd0, device_id};
    end
end

always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        status_reg <= 'd0;
    end
    else if(start_flag)begin
        status_reg <= 'd0;
    end
    else if(op_done_i)begin
        status_reg <= 'd1;
    end
    else if(op_err_i)begin
        status_reg <= {27'd0, err_state_i, 1'b1, 1'b0};
    end
end

// 设置spi接口时钟的分频系数
assign clk_div_set      = config_reg[0];
assign clk_div_reset    = config_reg[1];
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        clk_div_o <= 32'd4;
    end
    else if(clk_div_reset)begin:reset_clk_div
        clk_div_o <= 32'd4;
    end
    else if(clk_div_set)begin:set_clk_div
        clk_div_o <= spiclk_div_reg;
    end
end

// 设置时钟模式
assign clk_mode_set     = config_reg[2];
assign clk_mode_reset   = config_reg[3];
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        clk_mode_o      <= 1'b1;
    end
    else if(clk_mode_reset)begin:reset_clk_mode
        clk_mode_o      <= 1'b1;
    end
    else if(clk_mode_set)begin
        clk_mode_o      <= spiclk_mode_reg[0];
    end
end

// 指令寄存器译码
assign start_flag       = start_reg[0];
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        w1B_req_o       <= 1'b0;
        r_req_o         <= 1'b0;
        r_sreg1_req_o   <= 1'b0;
        r_id_req_o      <= 1'b0;
        ce_req_o        <= 1'b0;
        se_req_o        <= 1'b0;
        be32_req_o      <= 1'b0;
        be64_req_o      <= 1'b0;
    end
    else if(op_done_i || op_err_i)begin
        w1B_req_o       <= 1'b0;
        r_req_o         <= 1'b0;
        r_sreg1_req_o   <= 1'b0;
        r_id_req_o      <= 1'b0;
        ce_req_o        <= 1'b0;
        se_req_o        <= 1'b0;
        be32_req_o      <= 1'b0;
        be64_req_o      <= 1'b0;
    end
    else if(start_flag)begin
        case (comd_reg[3:0])
            INST_NONE       : begin
                            w1B_req_o       <= 1'b0;
                            r_req_o         <= 1'b0;
                            r_sreg1_req_o   <= 1'b0;
                            r_id_req_o      <= 1'b0;
                            ce_req_o        <= 1'b0;
                            se_req_o        <= 1'b0;
                            be32_req_o      <= 1'b0;
                            be64_req_o      <= 1'b0;
            end
            INST_WRITE1B    : begin
                            w1B_req_o       <= 1'b1;
                            r_req_o         <= 1'b0;
                            r_sreg1_req_o   <= 1'b0;
                            r_id_req_o      <= 1'b0;
                            ce_req_o        <= 1'b0;
                            se_req_o        <= 1'b0;
                            be32_req_o      <= 1'b0;
                            be64_req_o      <= 1'b0;
            end
            INST_READ       : begin
                            w1B_req_o       <= 1'b0;
                            r_req_o         <= 1'b1;
                            r_sreg1_req_o   <= 1'b0;
                            r_id_req_o      <= 1'b0;
                            ce_req_o        <= 1'b0;
                            se_req_o        <= 1'b0;
                            be32_req_o      <= 1'b0;
                            be64_req_o      <= 1'b0;
            end
            INST_READ_SREG1 : begin
                            w1B_req_o       <= 1'b0;
                            r_req_o         <= 1'b0;
                            r_sreg1_req_o   <= 1'b1;
                            r_id_req_o      <= 1'b0;
                            ce_req_o        <= 1'b0;
                            se_req_o        <= 1'b0;
                            be32_req_o      <= 1'b0;
                            be64_req_o      <= 1'b0;
            end
            INST_READID     : begin
                            w1B_req_o       <= 1'b0;
                            r_req_o         <= 1'b0;
                            r_sreg1_req_o   <= 1'b0;
                            r_id_req_o      <= 1'b1;
                            ce_req_o        <= 1'b0;
                            se_req_o        <= 1'b0;
                            be32_req_o      <= 1'b0;
                            be64_req_o      <= 1'b0;
            end
            INST_CE         : begin
                            w1B_req_o       <= 1'b0;
                            r_req_o         <= 1'b0;
                            r_sreg1_req_o   <= 1'b0;
                            r_id_req_o      <= 1'b0;
                            ce_req_o        <= 1'b1;
                            se_req_o        <= 1'b0;
                            be32_req_o      <= 1'b0;
                            be64_req_o      <= 1'b0;
            end
            INST_SE         : begin
                            w1B_req_o       <= 1'b0;
                            r_req_o         <= 1'b0;
                            r_sreg1_req_o   <= 1'b0;
                            r_id_req_o      <= 1'b0;
                            ce_req_o        <= 1'b0;
                            se_req_o        <= 1'b1;
                            be32_req_o      <= 1'b0;
                            be64_req_o      <= 1'b0;
            end
            INST_BE32       : begin
                            w1B_req_o       <= 1'b0;
                            r_req_o         <= 1'b0;
                            r_sreg1_req_o   <= 1'b0;
                            r_id_req_o      <= 1'b0;
                            ce_req_o        <= 1'b0;
                            se_req_o        <= 1'b0;
                            be32_req_o      <= 1'b1;
                            be64_req_o      <= 1'b0;
            end
            INST_BE64       : begin
                            w1B_req_o       <= 1'b0;
                            r_req_o         <= 1'b0;
                            r_sreg1_req_o   <= 1'b0;
                            r_id_req_o      <= 1'b0;
                            ce_req_o        <= 1'b0;
                            se_req_o        <= 1'b0;
                            be32_req_o      <= 1'b0;
                            be64_req_o      <= 1'b1;
            end
            default         : begin
                            w1B_req_o       <= 1'b0;
                            r_req_o         <= 1'b0;
                            r_sreg1_req_o   <= 1'b0;
                            r_id_req_o      <= 1'b0;
                            ce_req_o        <= 1'b0;
                            se_req_o        <= 1'b0;
                            be32_req_o      <= 1'b0;
                            be64_req_o      <= 1'b0;
            end
        endcase
    end
end

// flash地址输出
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        addr_o          <= 'd0;
    end
    else if(op_done_i || op_err_i)begin
        addr_o          <= 'd0;
    end
    else if(start_flag)begin
        case (comd_reg)
            INST_WRITE1B        : addr_o <= addr_reg;
            INST_READ           : addr_o <= addr_reg;
            INST_CE             : addr_o <= addr_reg;
            INST_SE             : addr_o <= addr_reg;
            INST_BE32           : addr_o <= addr_reg;
            INST_BE64           : addr_o <= addr_reg;
            default             : addr_o <= 32'd0   ;
        endcase
    end
end

// 写或读数据长度，单位为字节
always@(posedge pclk_i or negedge prstn_i)begin
    if(!prstn_i)begin
        data_len_o      <= 'd1;
    end
    else if(op_done_i || op_err_i)begin
        data_len_o      <= 'd1;
    end
    else if(start_flag)begin
        case (comd_reg)
            INST_WRITE1B        : data_len_o <= data_len_reg;
            INST_READ           : data_len_o <= data_len_reg;
            INST_READID         : data_len_o <= data_len_reg;
            default             : data_len_o <= 'd1     ;
        endcase
    end
end

endmodule
